<?php
require_once(dirname(__FILE__)."/../common/constants.php");
require_once(COMMON_DIR_PATH."/xml_common.php");
require_once(DB_SESSION_DIR_PATH."/database_common.php");
require_once(DB_SESSION_DIR_PATH."/db_common.php");
require_once(DB_SESSION_DIR_PATH."/db_error.php");

class DatabaseOperator {
	private $config;
	private $conn_obj;
	
	public function __construct() {
		$this->config 	= array();
		$this->conn_obj = NULL;
	}
	
	public function config($config_file) {
		if (gettype($config_file) == gettype(array())) {
			foreach ($config_file as $cf)
				$this->single_config($cf, true);
		} else
			$this->single_config($config_file, false);
	}
	
	private function single_config($config_file, $bAppend) {
		if (!$bAppend)
			$this->config = array();
		
		if ($config_file instanceof SqlAlias) {
			$this->config[] = GetDataQueryConfig($config_file->getOrig(), $config_file->getAlias());
		} else {
			$this->config[] = GetDataQueryConfig($config_file);
		}
	}
	
	private function validate_config() {
		if ($this->config === NULL)
			return FALSE;
		else
			return TRUE;
	}
	
	private function connect2db() {
		if ($this->conn_obj === NULL) {
			$this->conn_obj = new MySqlConnector( $this->config[0]->getHost(), 
											$this->config[0]->getUser(), 
											$this->config[0]->getPassword(), 
											$this->config[0]->getDatabase() );
			$this->conn_obj->connect();
		}
	}
	
	private function getTablesFromConfig() {
		$t = '';
		foreach ($this->config as $cf)
			$t .= $cf->getTable() . ',';
			
		return rtrim($t, ",");
	}
	/*
	 * @$targets: the columns you want to select.
	 * @$conditions: the where condition.
	 * Returned value: the array containing the columns returned from database.
	 */
	public function select($conditions, $targets = NULL) {
		if (!$this->validate_config())
			return array();		
		if ($targets === NULL) {
			$targets = array();
			$targets[] = SqlConstants :: ASTERISK;
		}
		
		if (!count($targets)) {
			return array();
		}
		
		if (gettype($targets) != gettype(array())) {
			return array();
		}
		
		if (gettype($conditions) != gettype(new SqlCondList())) {
			return array();
		}
		
		// connect to database
		$this->connect2db();
		if ($this->conn_obj->get_error() != NULL) {
			return DbError::MySqlError2LocalError($this->conn_obj->get_error());
		}
		
		$table = $this->getTablesFromConfig();
		
		if (in_array(SqlConstants::ASTERISK, $targets)) {
			$cols = " ".SqlConstants::ASTERISK." ";
		} else {
			$cols = "";
			/*
			$keys = array_keys($targets);
			foreach ($keys as $key) {
				$cols = $cols.$targets[$key];
				$cols = $cols.",";
			}
			*/
			foreach ($targets as $col) {
				if ($col instanceof SqlAlias)
					$cols .= $col->sqlize();
				else
					$cols .= $col;
				$cols .= ",";
			}
			$cols = rtrim($cols, ",");
			$cols = $cols."";
		}		
		
		// Generate the conditions part of the update SQL instruction
		$where = $conditions->sqlize();
		
		$select = "select $cols from $table $where";
		//echo $select."<p>";
		$query_result = $this->conn_obj->execute($select);
		if ($this->conn_obj->get_error() != NULL) {
			return DbError::MySqlError2LocalError($this->conn_obj->get_error());
		}
		$ret = array();
		while ($row = mysql_fetch_array($query_result, MYSQL_ASSOC))
		{
			$ret[] = $row;
		}
		return $ret;
	}
	public function insert($kv_pairs, $require_auto_incremented_uid = TRUE) {
		if (!$this->validate_config())
			return DbError::INVALID_DB_QUERY_CONFIGURATION;
		
		
		$this->connect2db();
		if ($this->conn_obj->get_error() != NULL) {
			return DbError::MySqlError2LocalError($this->conn_obj->get_error());
		}
		$table = $this->getTablesFromConfig();
		
		if (gettype($this->config) == gettype(array())) {
			foreach ($this->config as $c)
				$missed_cols = validate_mandatory_columns($kv_pairs, $c->getColumns());
		} else
			$missed_cols = validate_mandatory_columns($kv_pairs, $this->config->getColumns());

		if (count($missed_cols)) {
			$error = "Mandatory columns must be provided: ";
			foreach ($missed_cols as $c) {
				$error = $error.$c.' ';
				echo $error;
			}
			return DbError::MANDATORY_COLUMNS_REQUIRED;
		}
		
		$keys = array_keys($kv_pairs);
		$cols = "(";
		$values = "(";
		foreach ($keys as $key) {
			$cols = $cols.$key;
			$cols = $cols.",";
			
			if ($kv_pairs[$key] === NULL) {
				$values = $values.'NULL,';
			} else {
				
				$values = $values.'"';
				$values = $values.$kv_pairs[$key];
				$values = $values.'",';
			}
		}
		$cols = rtrim($cols, ",");
		$cols = $cols.")";
		$values = rtrim($values, ",");
		$values = $values.")";
		
		$create_sql = "insert into $table $cols values $values";
		//echo $create_sql."<p>";
		$query_result = $this->conn_obj->execute($create_sql);
		if ($this->conn_obj->get_error() != NULL) {
			$retno = DbError::MySqlError2LocalError($this->conn_obj->get_error());
			return $retno;
		}
		if (!$query_result) {
			echo "Failed to insert the record.";
			return DbError::INSERT_ERROR;
		}
		
		if($require_auto_incremented_uid)
			return $this->conn_obj->get_auto_incremented_id();
		else
			return DbError::DB_OK;
	}
	
	/*
	 * Return value: DB_OK if updating succeeds.
	 */
	public function update($updated_array, $conditions)
	{
		if (!$this->validate_config())
			return DbError::INVALID_DB_QUERY_CONFIGURATION;
			
		// Check the necessity of the updating
		if (count($updated_array) == 0) {
			return DbError::DB_OK;
		}
		if (gettype($conditions) != gettype(new SqlCondList())) {
			return DbError::INVALID_PARAMETER;
		}
		
		$this->connect2db();
		if ($this->conn_obj->get_error() != NULL) {
			return DbError::MySqlError2LocalError($this->conn_obj->get_error());
		}
		$table = $this->getTablesFromConfig();
		
		// Generate the set portion of the update SQL instruction
		$keys = array_keys($updated_array);
		$set = '';
		foreach ($keys as $key) {
			if ($updated_array[$key] == 0) {
				if ($updated_array[$key] === NULL)
					$set .= $key.'=NULL,';
				else
					$set .= $key.'="'.$updated_array[$key].'",';
			} else {
				if ($updated_array[$key] != NULL) {
					if ($updated_array[$key] instanceof DeltaValue) {
						$set .= $key.'='.$key.'+"'.$updated_array[$key]->getValue().'",';
					} else
						$set .= $key.'="'.$updated_array[$key].'",';
				} else
					$set .= $key.'=NULL,';
			}
		}
		$set = rtrim($set, ",");
		
		// Generate the conditions part of the update SQL instruction
		$where = $conditions->sqlize();
		
		$update_sql = "update $table set $set $where";
		//echo $update_sql."<p>";
		$query_result = $this->conn_obj->execute($update_sql);
		if ($this->conn_obj->get_error() != NULL) {
			return DbError::MySqlError2LocalError($this->conn_obj->get_error());
		}
		if (!$query_result) {
			return DbError::UPDATE_ERROR;
		}
		return DbError::DB_OK;
	}
	
	public function delete($conditions = NULL)
	{
		if (!$this->validate_config())
			return DbError::INVALID_DB_QUERY_CONFIGURATION;
			
		if (($conditions != NULL) &&
			(gettype($conditions) != gettype(new SqlCondList()))) {
			echo '$conditions parameter should be of SqlCondList type.';
			return DbError::INVALID_PARAMETER;
		}
		
		$this->connect2db();
		if ($this->conn_obj->get_error() != NULL) {
			return DbError::MySqlError2LocalError($this->conn_obj->get_error());
		}
		$table = $this->getTablesFromConfig();
		
		// Generate the set portion of the update SQL instruction
		
		// Generate the conditions part of the update SQL instruction
		$where = "";
		if ($conditions != NULL)
			$where = $conditions->sqlize();
		
		$update_sql = "delete from $table $where";
		$query_result = $this->conn_obj->execute($update_sql);
		if ($this->conn_obj->get_error() != NULL) {
			return DbError::MySqlError2LocalError($this->conn_obj->get_error());
		}
		if (!$query_result) {
			echo "Failed to delete the specified record.";
			return DbError::DELETE_ERROR;
		}
		
		return DbError::DB_OK;
	}
}
?>